package com.yilin.tms.core.commons.utils;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yilin.commons.exception.BusinessException;
import com.yilin.commons.util.FieldUtil;
import com.yilin.commons.util.StringUtil;
import com.yilin.tms.core.application.AppUtil;
import com.yilin.tms.core.commons.base.BaseEntity;
import javax.persistence.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 动态查询工具类
 */
public class QueryUtil {

    private static final String AND = "&";
    private static final String OR = "\\|";
    private static final String EQ = "=";
    private static final String NUMBER_RANGE = "<->";
    private static final String DATE_RANGE = "<~>";
    private static final String KEYWORD = "keyword";
    private static final String SORT = "-";
    private static final String DEFAULT_SORT_FIELD = "create_time";



    /**
     * TODO:使用前必须看下面的解释
     * 使用MyBatis-Plus自带的条件构造器进行动态查询和排序
     * ~时间区间 -数字区间
     * <p>
     * s=xxx&d=xxx
     */
    public static <T extends BaseEntity> QueryWrapper<T> andQueryWrapper(QueryWrapper<T> parentWrapper, T obj) {
        if (obj == null) return parentWrapper;
        String query = obj.getQuery(), sort = obj.getSort();
        if (StringUtil.isNotEmpty(query)) {
            //得到查询字段和值
            String[] fieldValues = query.split(AND);
            for (String fieldValue : fieldValues) {
                String[] tvs = fieldValue.split(EQ);
                if (tvs.length != 2) continue;
                String field = tvs[0];//查询字段
                String colName = getColumnName(obj, field);//这里得到行字段
                String[] values = tvs[1].split(OR);//查询值
                parentWrapper.and(queryWrapper -> {
                    for (int i = 0; i < values.length; i++) {
                        if (!field.endsWith(KEYWORD)) {
                            //区间查询 数字/时间 1-2
                            if (values[i].contains(NUMBER_RANGE) || values[i].contains(DATE_RANGE)) {
                                String[] strings;
                                if (values[i].contains(DATE_RANGE)) strings = values[i].split(DATE_RANGE);
                                else strings = values[i].split(NUMBER_RANGE);
                                if (strings.length != 2) throw new BusinessException("区间查询参数不全");
                                if (values[i].contains(DATE_RANGE)) {//时间区间
                                    queryWrapper.apply(String.format(" %s BETWEEN '%s 00:00:00' AND '%s 23:59:59' ", colName, strings[0], strings[1]));
                                } else {
                                    queryWrapper.apply(String.format(" %s BETWEEN %s AND %s ", colName, strings[0], strings[1]));
                                }
                            } else getQueryWrapper(queryWrapper, obj.getClass(), field, colName, values[i]);//指定模糊查询
                        }
                        //关键字模糊查询
                        else {
                            List<String> keyFields = getStringCols(obj);
                            for (int i1 = 0; i1 < keyFields.size(); i1++) {
                                queryWrapper.like(keyFields.get(i1), values[i]);
                                if (i1 < keyFields.size() - 1) queryWrapper.or();
                            }
                        }
                        if (i < values.length - 1) queryWrapper.or();
                    }
                });
            }
        }

        //动态排序
        if (StringUtil.isNotEmpty(sort)) {
            String[] sortFs = sort.split(OR);
            for (String sortF : sortFs) {
                String[] sortFvs = sortF.split(SORT);
                if (sortFvs.length != 2 || StringUtil.isEmpty(sortFvs[0]) || StringUtil.isEmpty(sortFvs[1]))
                    throw new BusinessException("排序条件错误");
                String colName = getColumnName(obj, sortFvs[0]);//检查字段
                String values = "ASC,DESC";//检查值
                if (!Arrays.asList(values.split(",")).contains(sortFvs[1])) {
                    throw new BusinessException("前端数据出错，排序值只能是 " + values);
                }
                if (sortFvs[1].equals("DESC")) parentWrapper.orderByDesc(colName);
                else parentWrapper.orderByAsc(colName);
            }
        } else parentWrapper.orderByDesc(DEFAULT_SORT_FIELD);
        return parentWrapper;
    }

    /**
     * TODO:使用前必须看下面的解释
     * <p>
     * 添加查询Sql
     * 遵循 类名大写 表名需重新AS为类名首字母小写
     * 如 类FeeConfig 表cfg_fee_config 重命名feeConfig 或直接传入tabName
     * ~时间区间 -数字区间
     */
    public static String getQuerySql(Object obj) {
        return getQuerySql(obj, null);
    }

    public static String getQuerySql(Object obj, String tabName) {
        if (obj == null) return "";
        String query = "";
        if (obj instanceof BaseEntity) query = ((BaseEntity) obj).getQuery();
        if (StringUtil.isEmpty(query)) return "";
        StringBuilder sql = new StringBuilder("");
        //获取数据表名称
        Entity entity = obj.getClass().getAnnotation(Entity.class);
        if (tabName == null) tabName = getObjName(obj.getClass().getName());//这里得到表
        if (StringUtil.isNotEmpty(query)) {
            //得到查询字段和值
            String[] fieldValues = query.split(AND);
            for (String fieldValue : fieldValues) {
                String[] tvs = fieldValue.split(EQ);
                if (tvs.length != 2) continue;
                String field = tvs[0];//查询字段
                String colName = getColumnName(obj, field);//这里得到行字段
                String[] values = tvs[1].split(OR);//查询值
                sql.append(" AND ( ");
                for (int i = 0; i < values.length; i++) {
                    if (!field.endsWith(KEYWORD)) {
                        if (values[i].contains(NUMBER_RANGE) || values[i].contains(DATE_RANGE)) { //TODO:1.区间查询
                            String[] strings;
                            if (values[i].contains(DATE_RANGE)) strings = values[i].split(DATE_RANGE);
                            else strings = values[i].split(NUMBER_RANGE);
                            if (strings.length != 2) return "";
                            if (values[i].contains(DATE_RANGE)) {//时间区间
                                sql.append(String.format("( %s.%s BETWEEN '%s 00:00:00' AND '%s 23:59:59' )", tabName, colName, strings[0], strings[1]));
                            } else {
                                sql.append(String.format("( %s.%s BETWEEN %s AND %s  )", tabName, colName, strings[0], strings[1]));
                            }
                        } else {//TODO:2.指定模糊查询
                            sql.append(String.format(" " + tabName + ".%s", QueryUtil.getQuery(obj.getClass(), field,
                                    colName, values[i])));
                        }
                    } else {//TODO:3.关键字模糊查询
                        List<String> keyFields = getStringCols(obj);
                        for (int j = 0; j < keyFields.size(); j++) {
                            sql.append(String.format(tabName + "." + keyFields.get(j) + " LIKE %s ", "'%" +
                                    StringUtil.trimStr(values[i]) + "%'"));
                            if (j < keyFields.size() - 1) sql.append(" OR ");
                        }
                    }
                    if (i < values.length - 1) sql.append(" OR ");
                }
                sql.append(" ) ");
            }
            sql.append(" AND ").append(tabName).append(".is_deleted = 0 ");
        }
        return sql.toString();
    }

    /**
     * 添加排序SQL
     * 遵循 类名大写 实例化同名首字母小写
     * 如 FeeConfig feeConfig
     */
    public static String getSortSql(Object obj) {
        return getSortSql(obj, null);
    }

    public static String getSortSql(Object obj, String tabName) {
        if (obj == null) return "";
        String sorts = "";
        if (obj instanceof BaseEntity) sorts = ((BaseEntity) obj).getSort();
        if (StringUtil.isEmpty(sorts)) return "";
        if (tabName == null) tabName = getObjName(obj.getClass().getName());
        String[] sortFs = sorts.split(OR);
        StringBuilder sql = new StringBuilder(" ORDER BY ");
        for (int i = 0; i < sortFs.length; i++) {
            String[] sortFvs = sortFs[i].split(SORT);
            if (sortFvs.length != 2 || StringUtil.isEmpty(sortFvs[0]) || StringUtil.isEmpty(sortFvs[1])) {
                return "";
            }
            String colName = getColumnName(obj, sortFvs[0]);//这里得到行字段
            String values = "ASC,DESC";//检查值
            if (!Arrays.asList(values.split(",")).contains(sortFvs[1])) {
                throw new BusinessException("前端数据出错，排序值只能是 " + values);
            }
            sql.append(String.format(" " + tabName + ".%s %s", colName, sortFvs[1]));
            if (i < sortFs.length - 1) sql.append(",");
        }
        return sql.toString();
    }

    /**
     * 获取字段名
     */
    private static String getColumnName(Object obj, String field) {
        if (StringUtil.isEmpty(field)) throw new BusinessException("前端数据出错，查询字段不能为空，关键字字段请用：keyword");
        if (field.endsWith("keyword")) return "keyword";
        Field javaField = FieldUtil.getField(obj.getClass(), field);
        if (javaField == null) throw new BusinessException("前端数据出错，实体/表不包含" + field + "字段");
        Column annotation = javaField.getAnnotation(Column.class);
        if (annotation == null) throw new BusinessException("前端数据出错，" + field + "字段不是数据库字段，不支持查询");
        if (StringUtil.isEmpty(annotation.name())) return field;
        return annotation.name();
    }

    /**
     * 获取实例化名称
     * 查询工具类
     */
    private static String getObjName(String objName) {
        if (objName.contains(".")) {
            String[] splits = objName.split("\\.");
            objName = splits[splits.length - 1];
            objName = AppUtil.toLowerCaseFirstOne(objName);
        }
        return objName;
    }

    /**
     * 获取所有字符串字段
     * 查询工具类
     */
    private static List<String> getStringFields(Object obj) {
        List<String> stringFields = new ArrayList<>();
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field f : fields) {
            if (f.getType().equals(String.class)) {
                Column annotation = f.getAnnotation(Column.class);
                if (annotation != null) {
                    stringFields.add(f.getName());
                }
            }
        }
        return stringFields;
    }

    /**
     * 获取所有字符串字段 表列
     * 查询工具类
     */
    private static List<String> getStringCols(Object obj) {
        List<String> stringCols = new ArrayList<>();
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field f : fields) {
            if (f.getType().equals(String.class)) {
                Column annotation = f.getAnnotation(Column.class);
                if (annotation != null) {
                    if (StringUtil.isEmpty(annotation.name())) {
                        stringCols.add(f.getName());
                    } else {
                        stringCols.add(annotation.name());
                    }
                }
            }
        }
        return stringCols;
    }

    /**
     * 获取查询值
     * 查询工具类
     */
    private static String getQuery(Class cls, String field, String colName, String queryValue) {
        Class aClass = FieldUtil.getFiledType(field, cls);
        String query = "";
        if (aClass == null) return query;
        if (queryValue.equals("null")) {
            query = colName + " IS NULL ";
        }
        if (aClass.isEnum()) {
            for (int i = 0; i < aClass.getEnumConstants().length; i++) {
                if (aClass.getEnumConstants()[i].toString().equals(queryValue)) {
                    return colName + " = " + i;
                }
            }
        } else if (aClass.equals(Boolean.class)) {
            Boolean value = Boolean.valueOf(queryValue);
            return colName + " = " + (value ? 1 : 0);
        } else {
            query = colName + " LIKE '%" + StringUtil.trimStr(queryValue) + "%' ";
        }
        return query;
    }

    /**
     * 查询工具类
     *
     * @param cls        实体类
     * @param field      属性名
     * @param colName    数据库字段名
     * @param queryValue 查询值
     */
    private static void getQueryWrapper(QueryWrapper queryWrapper, Class cls, String field, String colName, String queryValue) {
        Class aClass = FieldUtil.getFiledType(field, cls);
        if (queryValue.equals("null")) {
            queryWrapper.isNull(colName);
            return;
        }
        if (aClass.isEnum()) {
            for (int i = 0; i < aClass.getEnumConstants().length; i++) {
                if (aClass.getEnumConstants()[i].toString().equals(queryValue)) {
                    queryWrapper.eq(colName, i);
                }
            }
        } else if (aClass.equals(Boolean.class)) {
            Boolean value = Boolean.valueOf(queryValue);
            queryWrapper.eq(colName, value ? 1 : 0);
        } else {
            queryWrapper.like(colName, queryValue);
        }
    }


}
